import numpy as np
import torch


def random_bit_mask(device, Lind, number_type, gpu_number_type, prob_mut, size_pop, len_chrom, bit_precision=1):
    bit_precision = bit_precision + torch.ceil(-torch.log2(prob_mut)).int()
    mask_tmp0 = torch.randint(np.iinfo(number_type).min, np.iinfo(number_type).max,
                              (size_pop, len_chrom, bit_precision), device=device, dtype=gpu_number_type)

    bit_prob = (prob_mut * (1 << bit_precision)).type(gpu_number_type)
    mask = torch.zeros((size_pop, len_chrom), dtype=gpu_number_type, device=device)
    mask_tmp1 = torch.ones((size_pop, len_chrom), dtype=gpu_number_type, device=device) * (1 << Lind) - 1
    for i in range(bit_precision - 1, 0, -1):
        mutate_bit = ~mask_tmp0[:, :, i]
        high_bit = bit_prob >> i
        # use overflow to make constant_bit equal to all 0 or all 1(same as ith low bit_prob bit)
        constant_bit = (~high_bit & 1) - 1

        # if mutate_bit is 1 and constant_bit is 0 means random high bit > prob_mut high bit means comparation is not significant
        mask_tmp1 &= mutate_bit | constant_bit

        '''if mutate_bit is 1(random high bit is 0) and 
        constant_bit is 1(prob_mut high bit is 0) means mutate_bit lower than prob_mut and 
        mask_tmp1 is 1(compare is significant),the mask will enable'''
        mask |= mask_tmp1 & mutate_bit & constant_bit
    return mask
